package org.gdstash.db;

import org.gdstash.util.GDMsgFormatter;
import org.gdstash.util.GDMsgLogger;

import java.nio.ByteBuffer;
import java.nio.charset.StandardCharsets;
import java.sql.*;
import java.util.LinkedList;
import java.util.List;

public class DBStatBonusRace implements Cloneable {
    private static final String TABLE_NAME_ITEM = "GD_ITEM_RACES";
    private static final String TABLE_NAME_AFFIX = "GD_AFFIX_RACES";
    private static final String TABLE_NAME_SKILL = "GD_SKILL_RACES";
    private static final String FIELD_ID_ITEM = "ITEM_ID";
    private static final String FIELD_ID_AFFIX = "AFFIX_ID";
    private static final String FIELD_ID_SKILL = "SKILL_ID";
    private static final int ROW_ITEM_ID = 1;
    private static final int ROW_RACE_TAG = 2;
    private static final int ROW_RACE_NAME = 3;
    private String itemID;
    private String raceTag;
    private String raceName;

    public DBStatBonusRace() {
        this.itemID = null;
        this.raceTag = null;
        this.raceName = null;
    }

    public DBStatBonusRace(final DBStatBonusRace race) {
        this.itemID = race.itemID;
        this.raceTag = race.raceTag;
        this.raceName = race.raceName;
    }

    private static List<DBStatBonusRace> wrap(final ResultSet rs) throws SQLException {
        final LinkedList<DBStatBonusRace> list = new LinkedList<DBStatBonusRace>();
        while (rs.next()) {
            final DBStatBonusRace race = new DBStatBonusRace();
            race.itemID = rs.getString(1);
            race.raceTag = rs.getString(2);
            race.raceName = rs.getString(3);
            if (race.raceName != null) {
                final ByteBuffer buffer = StandardCharsets.UTF_8.encode(race.raceName);
                final String utf8 = StandardCharsets.UTF_8.decode(buffer).toString();
                race.raceName = utf8;
            }
            list.add(race);
        }
        return list;
    }

    public DBStatBonusRace clone() {
        final DBStatBonusRace race = new DBStatBonusRace(this);
        return race;
    }

    @Override
    public boolean equals(final Object o) {
        if (o == null) {
            return false;
        }
        if (!o.getClass().equals(DBStatBonusRace.class)) {
            return false;
        }
        final DBStatBonusRace br = (DBStatBonusRace) o;
        return this.raceTag.equals(br.raceTag);
    }

    public String getID() {
        return this.itemID;
    }

    public void setID(final String itemID) {
        this.itemID = itemID;
    }

    public String getRaceTag() {
        if (this.raceTag == null) {
            return null;
        }
        if (this.raceTag.startsWith("tag")) {
            return this.raceTag;
        }
        return "tag" + this.raceTag;
    }

    public void setRaceTag(final String raceTag) {
        this.raceTag = raceTag;
    }

    public String getRaceName() {
        return this.raceName;
    }

    public static void createItemTable(final Connection conn) throws SQLException {
        createTable(conn, "GD_ITEM_RACES", "ITEM_ID");
    }

    public static void createAffixTable(final Connection conn) throws SQLException {
        createTable(conn, "GD_AFFIX_RACES", "AFFIX_ID");
    }

    public static void createSkillTable(final Connection conn) throws SQLException {
        createTable(conn, "GD_SKILL_RACES", "SKILL_ID");
    }

    private static void createTable(final Connection conn, final String tabName, final String idName) throws SQLException {
        final String dropTable = "DROP TABLE " + tabName;
        final String createTable = "CREATE TABLE " + tabName + " (" + idName + " VARCHAR(" + "256" + ") NOT NULL, RACE_TAG           VARCHAR(" + "64" + "), RACE_NAME          VARCHAR(" + "256" + "), PRIMARY KEY (" + idName + ", RACE_TAG))";
        try (final Statement st = conn.createStatement()) {
            if (GDDBUtil.tableExists(conn, tabName)) {
                st.execute(dropTable);
            }
            st.execute(createTable);
            st.close();
            conn.commit();
        } catch (SQLException ex) {
            final Object[] args = {tabName};
            final String msg = GDMsgFormatter.format(GDMsgFormatter.rbMsg, "ERR_CREATE_TABLE", args);
            GDMsgLogger.addError(msg);
            throw ex;
        }
    }

    public static void deleteItem(final Connection conn, final String id) throws SQLException {
        delete(conn, "GD_ITEM_RACES", "ITEM_ID", id);
    }

    public static void deleteAffix(final Connection conn, final String id) throws SQLException {
        delete(conn, "GD_AFFIX_RACES", "AFFIX_ID", id);
    }

    public static void deleteSkill(final Connection conn, final String id) throws SQLException {
        delete(conn, "GD_SKILL_RACES", "SKILL_ID", id);
    }

    private static void delete(final Connection conn, final String tabName, final String idName, final String itemID) throws SQLException {
        final String deleteEntry = "DELETE FROM " + tabName + " WHERE " + idName + " = ?";
        try (final PreparedStatement ps = conn.prepareStatement(deleteEntry)) {
            ps.setString(1, itemID);
            ps.executeUpdate();
            ps.close();
        } catch (SQLException ex) {
            throw ex;
        }
    }

    public static void insertItem(final Connection conn, final String id, final List<DBStatBonusRace> races) throws SQLException {
        insert(conn, "GD_ITEM_RACES", id, races);
    }

    public static void insertAffix(final Connection conn, final String id, final List<DBStatBonusRace> races) throws SQLException {
        insert(conn, "GD_AFFIX_RACES", id, races);
    }

    public static void insertSkill(final Connection conn, final String id, final List<DBStatBonusRace> races) throws SQLException {
        insert(conn, "GD_SKILL_RACES", id, races);
    }

    private static void insert(final Connection conn, final String tabName, final String id, final List<DBStatBonusRace> races) throws SQLException {
        final String insert = "INSERT INTO " + tabName + " VALUES (?,?,?)";
        if (races == null) {
            return;
        }
        if (races.isEmpty()) {
            return;
        }
        try (final PreparedStatement ps = conn.prepareStatement(insert)) {
            for (final DBStatBonusRace race : races) {
                ps.setString(1, id);
                ps.setString(2, race.raceTag);
                ps.setString(3, race.raceName);
                ps.executeUpdate();
                ps.clearParameters();
            }
            ps.close();
        }
    }

    public static List<DBStatBonusRace> getItem(final String id) throws SQLException {
        return get("GD_ITEM_RACES", "ITEM_ID", id);
    }

    public static List<DBStatBonusRace> getAffix(final String id) throws SQLException {
        return get("GD_AFFIX_RACES", "AFFIX_ID", id);
    }

    public static List<DBStatBonusRace> getSkill(final String id) throws SQLException {
        return get("GD_SKILL_RACES", "SKILL_ID", id);
    }

    private static List<DBStatBonusRace> get(final String tableName, final String fieldName, final String id) {
        List<DBStatBonusRace> list = new LinkedList<DBStatBonusRace>();
        final String command = "SELECT * FROM " + tableName + " WHERE " + fieldName + " = ?";
        try (final Connection conn = GDDBData.getConnection();
             final PreparedStatement ps = conn.prepareStatement(command)) {
            ps.setString(1, id);
            try (final ResultSet rs = ps.executeQuery()) {
                list = wrap(rs);
                conn.commit();
            } catch (SQLException ex) {
                throw ex;
            }
        } catch (SQLException ex2) {
            final Object[] args = {id, tableName};
            final String msg = GDMsgFormatter.format(GDMsgFormatter.rbMsg, "ERR_READ_TABLE_BY_ID", args);
            GDMsgLogger.addError(msg);
            GDMsgLogger.addError(ex2);
        }
        return list;
    }

    public void setRaceName(final String raceName) {
        this.raceName = raceName;
    }
}
